﻿using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
    /// <summary>
    /// 增强的可空类型 Schema 过滤器，支持 C# 8+ 可空引用类型
    /// </summary>
    public class NullableSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (schema?.Properties == null)
                return;

            // 如果整个类型是可空的
            if (IsNullableType(context.Type))
            {
                schema.Nullable = true;
            }

            // 处理所有属性
            foreach (var property in context.Type.GetProperties())
            {
                // 尝试以多种方式获取属性名称的转换形式
                if (TryGetPropertySchema(schema, property, out var propertySchema))
                {
                    if (IsNullableProperty(property))
                    {
                        propertySchema.Nullable = true;
                    }
                }
            }
        }

        /// <summary>
        /// 尝试以多种命名约定获取属性 Schema
        /// </summary>
        /// <summary>
        /// 尝试以多种命名约定获取属性 Schema
        /// </summary>
        private bool TryGetPropertySchema(OpenApiSchema schema, PropertyInfo property, out OpenApiSchema propertySchema)
        {
            // 检查属性的 JsonPropertyName 特性
            var jsonPropertyNameAttr = property.GetCustomAttribute<JsonPropertyNameAttribute>();
            if (jsonPropertyNameAttr != null &&
                jsonPropertyNameAttr.Name != null &&
                schema.Properties.TryGetValue(jsonPropertyNameAttr.Name, out var schema1))
            {
                propertySchema = schema1;
                return true;
            }

            // 尝试直接使用属性名
            if (schema.Properties.TryGetValue(property.Name, out var schema2))
            {
                propertySchema = schema2;
                return true;
            }

            // 尝试使用 camelCase (JSON.NET 和 System.Text.Json 的默认规则)
            var camelCaseName = char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
            if (schema.Properties.TryGetValue(camelCaseName, out var schema3))
            {
                propertySchema = schema3;
                return true;
            }

            // 尝试全小写
            if (schema.Properties.TryGetValue(property.Name.ToLowerInvariant(), out var schema4))
            {
                propertySchema = schema4;
                return true;
            }

            propertySchema = new OpenApiSchema(); // 使用空对象替代null
            return false;
        }

        /// <summary>
        /// 检查类型是否可空
        /// </summary>
        private bool IsNullableType(Type type)
        {
            return type.IsClass ||
                   (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        /// <summary>
        /// 检查属性是否为可空
        /// </summary>
        private bool IsNullableProperty(PropertyInfo property)
        {
            // 检查是否为可空值类型
            bool isNullableValueType = property.PropertyType.IsGenericType &&
                                      property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);

            // 检查是否为引用类型
            bool isReferenceType = property.PropertyType.IsClass;

            // 获取可空引用类型特性 (C# 8+)
            bool hasNullableAttribute = property
                .CustomAttributes
                .Any(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute" ||
                             attr.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute");

            // 检查特定特性，如 Required
            bool isRequired = property.GetCustomAttributes()
                .Any(attr => attr.GetType().Name == "RequiredAttribute" ||
                             attr.GetType().Name == "JsonRequiredAttribute");

            // 如果是值类型，只有 Nullable<T> 是可空的
            // 如果是引用类型，默认都是可空的（除非标记了 Required 特性）
            return isNullableValueType || (isReferenceType && !isRequired);
        }
    }



    public class EnumOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            foreach (var parameter in operation.Parameters)
            {
                var parameterDescription = context.ApiDescription.ParameterDescriptions
                    .FirstOrDefault(p => p.Name == parameter.Name);

                if (parameterDescription == null)
                    continue;

                var parameterType = parameterDescription.Type;

                // 处理枚举类型参数
                if (parameterType != null && parameterType.IsEnum)
                {
                    parameter.Schema = new OpenApiSchema
                    {
                        Type = "string",
                        Enum = Enum.GetNames(parameterType)
                            .Select(name => new OpenApiString(name))
                            .ToList<IOpenApiAny>(),
                        Description = parameter.Description
                    };
                }
            }
        }
    }

    public class EnumWithNamesSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                // 获取所有枚举名称
                string[] names = Enum.GetNames(context.Type);
                // 获取所有枚举值
                Array values = Enum.GetValues(context.Type);

                // 添加标准枚举定义（字符串形式）
                schema.Enum = names.Select(n => new OpenApiString(n)).Cast<IOpenApiAny>().ToList();

                // 添加扩展属性，包含名称和值的映射
                var enumNamesArray = new OpenApiArray();
                foreach (var name in names)
                {
                    enumNamesArray.Add(new OpenApiString(name));
                }
                schema.Extensions["x-enumNames"] = enumNamesArray;

                // 添加扩展属性，包含值和名称的映射
                var enumValueNameMap = new OpenApiObject();
                for (int i = 0; i < values.Length; i++)
                {
                    string value = Convert.ToInt32(values.GetValue(i)).ToString();
                    string name = names[i];
                    enumValueNameMap.Add(value, new OpenApiString(name));
                }
                schema.Extensions["x-enumVarnames"] = enumValueNameMap;
            }
        }
    }
}